package org.eden.heartbeat.core.configuration;

import org.eden.heartbeat.api.Constants;
import org.eden.heartbeat.api.configuration.ApplicationConfiguration;
import org.eden.heartbeat.api.exception.ConfigurationException;
import org.eden.heartbeat.api.exception.ConfigurationNotFoundException;
import org.eden.heartbeat.api.model.MachineInfo;
import org.eden.heartbeat.core.persistence.ConfigurationDAO;
import org.eden.heartbeat.core.persistence.SimplePersistenceManager;
import org.eden.heartbeat.api.utils.HeartbeatStringUtils;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

/**
 * Class that holds the application configurations
 *
 * @author trsimoes
 * @version $Revision:$
 */
public class CoreApplicationConfiguration extends ApplicationConfiguration {

    /**
     * The remote machine host address.
     */
    public static String machineHost;

    /**
     * The remote machine port.
     */
    public static int machinePort;

    /**
     * The timeout value for the connection request.
     */
    public static Integer machineTimeOutMs;

    /**
     * Database driver (ex: org.h2.Driver).
     */
    public static String databaseDriver;

    /**
     * Database URL address (ex: jdbc:h2:file:heartbeat).
     */
    public static String databaseUrl;

    /**
     * Username to connect to the database.
     */
    public static String databaseUsername;

    /**
     * Password to connect to the database.
     */
    public static String databasePassword;

    /**
     * Time between each validation that the monitor does to verify if the mbean is still running.
     */
    public static int monitorInterval;

    /**
     * Time between heartbeats when the remote machine is online.
     */
    public static int heartbeatIntervalOnline;

    /**
     * Time between heartbeats when the remote machine is offline.
     */
    public static int heartbeatIntervalOffline;

    /**
     * Method that initializes the application configurations.
     *
     * @throws ConfigurationException thrown when an error occurs while loading the configurations.
     */
    public static void loadConfigurations() throws ConfigurationException, ConfigurationNotFoundException {
        ApplicationConfiguration.loadConfigurations();
        readFromFile();
        readFromDatabase();
    }

    private static void readFromFile() throws ConfigurationException, ConfigurationNotFoundException
    {
        Properties properties = new Properties();
        try
        {
            properties.load(ApplicationConfiguration.class.getResourceAsStream("/heartbeat.properties"));
        } catch (IOException e) {
            throw new ConfigurationException(e);
        }

        databaseDriver = properties.getProperty("db.driver");
        databaseUrl = properties.getProperty("db.url");
        databaseUsername = properties.getProperty("db.username");
        databasePassword = properties.getProperty("db.password");

        String jmxPortString = properties.getProperty("jmx.port");
        if (!HeartbeatStringUtils.isNullOrEmpty(jmxPortString))
            try {
                jmxPort = Integer.valueOf(jmxPortString).intValue();
            } catch (NumberFormatException e) {
                throw new ConfigurationNotFoundException("jmx.port");
            }
        else {
            throw new ConfigurationNotFoundException("jmx.port");
        }

        if (HeartbeatStringUtils.isNullOrEmpty(databaseDriver)) {
            throw new ConfigurationNotFoundException("db.driver");
        }

        if (HeartbeatStringUtils.isNullOrEmpty(databaseUrl)) {
            throw new ConfigurationNotFoundException("db.url");
        }

        if (databaseUsername == null) {
            throw new ConfigurationNotFoundException("db.username");
        }

        if (databasePassword == null)
            throw new ConfigurationNotFoundException("db.password");
    }

    private static void readFromDatabase() throws ConfigurationException, ConfigurationNotFoundException {

        SimplePersistenceManager persistenceManager = SimplePersistenceManager.getInstance();
        ConfigurationDAO configurationDAO = persistenceManager.getDaoInstance(ConfigurationDAO.class);

        Map<String, String> configurations;
        try {
            configurations = configurationDAO.getConfiguration();
        } catch (SQLException e) {
            throw new ConfigurationException(e);
        }

        machineHost = configurations.get("machine.host");

        if (HeartbeatStringUtils.isNullOrEmpty(machineHost)) {
            throw new ConfigurationNotFoundException("machine.host");
        }

        try {
            machinePort = Integer.valueOf(configurations.get("machine.port"));
        } catch (NumberFormatException e) {
            throw new ConfigurationNotFoundException("machine.port");
        }

        if (HeartbeatStringUtils.isNullOrEmpty(configurations.get("machine.timeout.ms"))) {
            machineTimeOutMs = Constants.REMOTE_MACHINE_DEFAULT_TIMEOUT;
        } else {
            machineTimeOutMs = Integer.valueOf(configurations.get("machine.timeout.ms"));
        }

        try {
            monitorInterval = Integer.valueOf(configurations.get("monitor.interval"));
        } catch (NumberFormatException e) {
            throw new ConfigurationNotFoundException("monitor.interval");
        }

        try {
            heartbeatIntervalOnline = Integer.valueOf(configurations.get("heartbeat.interval.online"));
        } catch (NumberFormatException e) {
            throw new ConfigurationNotFoundException("heartbeat.interval.online");
        }

        try {
            heartbeatIntervalOffline = Integer.valueOf(configurations.get("heartbeat.interval.offline"));
        } catch (NumberFormatException e) {
            throw new ConfigurationNotFoundException("heartbeat.interval.offline");
        }
    }

    public static MachineInfo getConfiguredRemoteMachine() {
        return new MachineInfo(CoreApplicationConfiguration.machineHost, CoreApplicationConfiguration.machinePort,
                CoreApplicationConfiguration.machineTimeOutMs);
    }
}